package com.freexiaoyu.freevideoplay;


import com.freexiaoyu.freevideoplay.bean.MessageEvent;
import com.freexiaoyu.freevideoplay.databinding.ActivityVideoFfmpegBinding;
import com.freexiaoyu.freevideoplay.ui.base.BaseActivity;
import com.freexiaoyu.freevideoplay.utils.LogUtil;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.upstream.BaseDataSource;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.upstream.UdpDataSource;

import android.media.MediaCodec;
import android.media.MediaFormat;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.logging.LogRecord;

import okio.Buffer;

public class VideoUDPAcivity extends BaseActivity<ActivityVideoFfmpegBinding> implements SurfaceHolder.Callback {

    private static final String TAG = "VideoUDPAcivity";
    private DatagramSocket udpSocket;
    private Thread udpReceiverThread = null;
    private SurfaceHolder surfaceHolder;
    private MediaCodec mediaCodec;
    private byte[] videoBuffer; // Large buffer to hold video frames
    DatagramPacket packet;

    @Override
    protected int provideContentViewId() {
        return R.layout.activity_video_play;
    }

    @Override
    protected void init() {

    }

    @Override
    protected void initView() {
        SurfaceView surfaceView = findViewById(R.id.surfaceView);
        surfaceHolder = surfaceView.getHolder();
        surfaceHolder.addCallback(this);
    }

    int frameLength = 0;

    @Override
    protected void initData() {
        udpReceiverThread = new Thread(() -> {
            //receiveVideoStream();
            receiveVideoStreamNew();
        });
        udpReceiverThread.start();
    }

    @Override
    protected void initListener() {

    }

    @Override
    protected void messageHandler(MessageEvent event) {

    }


    private void processReceivedData(byte[] data) {

    }


    private static final int CLIENT_PORT = 22001; // 客户端端口
    private static final int BUFFER_SIZE = 1024 * 1024; // 缓冲区大小
    private static final int PLAY_TIMEOUT = 5000; // 超时时间常量

    public void receiveVideoStream() {
        try (DatagramSocket socket = new DatagramSocket(CLIENT_PORT)) {
            System.out.println("等待UDP数据包...");

            byte[] recbuff = new byte[BUFFER_SIZE];
            DatagramPacket packet = new DatagramPacket(recbuff, recbuff.length);

            // 第一阶段：接收前两个字节以确定预期长度
            int expectedLength = 2;
            int recvedLength = 0;

            while (recvedLength < expectedLength) {
                socket.setSoTimeout(PLAY_TIMEOUT);
                socket.receive(packet);
                //System.arraycopy(packet.getData(), 0, recbuff, recvedLength, packet.getLength());
                recvedLength += packet.getLength();
            }

            expectedLength = ((recbuff[0] & 0xFF) << 8) | (recbuff[1] & 0xFF);

            // 第二阶段：接收剩余的数据
            while (recvedLength < expectedLength) {
                socket.setSoTimeout(PLAY_TIMEOUT);
                socket.receive(packet);
                //System.arraycopy(packet.getData(), 0, recbuff, recvedLength, packet.getLength());
                recvedLength += packet.getLength();
            }


            byte[] recbuffData = new byte[expectedLength - 14];
            /***
             * src：源数组，即要从中复制元素的数组。
             * srcPos：源数组中开始复制的起始位置（索引），索引从 0 开始。
             * dest：目标数组，即要将元素复制到的数组。
             * destPos：目标数组中开始粘贴元素的起始位置（索引），索引从 0 开始。
             * length：要复制的元素个数。
             * */
            System.arraycopy(recbuff, 14, recbuffData, 0, expectedLength - 14);
            decodeVideoFrame(recbuffData);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    byte[] bytes;

    private void receiveVideoStreamNew() {
        try (DatagramSocket socket = new DatagramSocket(CLIENT_PORT);
             Buffer buffer = new Buffer()) {

            byte[] recBuffer = new byte[BUFFER_SIZE];
            DatagramPacket packet = new DatagramPacket(recBuffer, BUFFER_SIZE);
            int size = 0;
            while (!Thread.currentThread().isInterrupted()) {
                socket.receive(packet);
                buffer.write(recBuffer, packet.getOffset(), packet.getLength());
                if (size == 0) {
                    if (buffer.request(2)) {
                        size = ((buffer.readByte() & 0xFF) << 8) | (buffer.readByte() & 0xFF);
                    }
                }
                LogUtil.d("size","size:"+size+"  --- buffer:"+buffer.size());
                if (buffer.request(size)) {
                //if (buffer.size() == size) {
                    bytes = buffer.readByteArray(size);
                    size = 0;
                    buffer.clear();
                    Message msg = mHandler.obtainMessage();
                    msg.what = 1;
                    mHandler.sendMessage(msg);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void surfaceCreated(@NonNull SurfaceHolder holder) {
        configureDecoder(holder.getSurface());
    }

    @Override
    public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
    }

    @Override
    public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
        releaseDecoder();
    }

    private void configureDecoder(Surface surface) {
        try {
            mediaCodec = MediaCodec.createDecoderByType("video/avc");
            MediaFormat format = MediaFormat.createVideoFormat("video/avc", 1920, 1080); // Adjust resolution as needed
            mediaCodec.configure(format, surface, null, 0);
            mediaCodec.start();
        } catch (IOException e) {
            Log.e(TAG, "Failed to create decoder", e);
        }
    }

    private void releaseDecoder() {
        if (mediaCodec != null) {
            mediaCodec.stop();
            mediaCodec.release();
            mediaCodec = null;
        }
    }

    private final android.os.Handler mHandler = new android.os.Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            if (bytes != null) {
                byte[] bytesNew = Arrays.copyOfRange(bytes, 14, bytes.length - 14);
                decodeVideoFrame(bytesNew);
            }
            return false;
        }
    });


    private void decodeVideoFrame(byte[] data) {
        try {
            int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1);
            if (inputBufferIndex >= 0) {
                ByteBuffer inputBuffer = mediaCodec.getInputBuffer(inputBufferIndex);
                inputBuffer.clear();
                inputBuffer.put(data);
                mediaCodec.queueInputBuffer(inputBufferIndex, 0, data.length, 0, 0);
            }
            MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
            int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
            while (outputBufferIndex >= 0) {
                mediaCodec.releaseOutputBuffer(outputBufferIndex, true);
                outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
            }
        } catch (Exception ex) {
            Log.e(TAG, "异常", ex);
        }
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (udpSocket != null && !udpSocket.isClosed()) {
            udpSocket.close();
        }
        releaseDecoder();
    }
}
